package com.mo.tools.date;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.Date;

/**
 * @author: 杨兴付
 * @date: 2022/1/7 16:16
 * @return:
 * @throws: java.lang.Exception
 * @modificed by:
 * support custom format pattern and simple dateformat pattern
 * eg:
 * "2021-05-28T","yyyy-MM-dd'T'"
 * "2021-05-28T05:02","yyyy-MM-dd'T'HH:mm"
 * "2021-05-28T05:02:33","yyyy-MM-dd'T'HH:mm:ss"
 * "2021-05-28T05:02:33Z","yyyy-MM-dd'T'HH:mm:ss'Z'"
 * "2021-05-28T05:02:33.280","yyyy-MM-dd'T'HH:mm:ss.SSS"
 * "2021-05-28T05:02:33.280Z","yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
 * "20210528","yyyyMMdd"
 * "202105280502","yyyyMMddHHmm"
 * "20210528050233","yyyyMMddHHmmss"
 * "2021-05-28","yyyy-MM-dd"
 * "2021-05-28 05:02","yyyy-MM-dd HH:mm"
 * "2021-05-28 05:02:33","yyyy-MM-dd HH:mm:ss"
 * "2021-05-28 05:02:33.280","yyyy-MM-dd HH:mm:ss.SSS"
 * "2021/05/28","yyyy/MM/dd"
 * "2021/05/28 05:02","yyyy/MM/dd HH:mm"
 * "2021/05/28 05:02:33","yyyy/MM/dd HH:mm:ss"
 * "2021/05/28 05:02:33.280","yyyy/MM/dd HH:mm:ss.SSS"
 * "2021.05.28","yyyy.MM.dd"
 * "2021.05.28 05:02","yyyy.MM.dd HH:mm"
 * "2021.05.28 05:02:33","yyyy.MM.dd HH:mm:ss"
 * "2021.05.28 05:02:33.280","yyy.MM.dd HH:mm:ss.SSS"
 * "2021年05月28日","yyyy年MM月dd日"
 * "2021年05月28日 05时02分","yyyy年MM月dd日 HH时mm分"
 * "2021年05月28日 05时02分06秒","yyyy年MM月dd日 HH时mm分ss秒"
 */
public class LocalDateTimeUtils {

    public static ZoneId ZONE_ID = ZoneId.systemDefault();

    public enum FormatType {
        ISO_DATE("yyyy-MM-dd'T'"),
        ISO_DATE_TIME_MINUTE("yyyy-MM-dd'T'HH:mm"),
        ISO_DATE_TIME("yyyy-MM-dd'T'HH:mm:ss"),
        ISO_DATE_TIME_INSTANT("yyyy-MM-dd'T'HH:mm:ss'Z'"),
        ISO_DATE_TIME_NANO("yyyy-MM-dd'T'HH:mm:ss.SSS"),
        ISO_DATE_TIME_NANO_INSTANT("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
        BASIC_ISO_DATE("yyyyMMdd"),
        BASIC_ISO_DATE_DATE_TIME_MINUTE("yyyyMMddHHmm"),
        BASIC_ISO_DATE_DATE_TIME("yyyyMMddHHmmss"),
        DEFAULT_DATE("yyyy-MM-dd"),
        DEFAULT_DATE_MONTH("yyyy-MM"),
        DEFAULT_DATE_TIME_MINUTE("yyyy-MM-dd HH:mm"),
        DEFAULT_DATE_TIME("yyyy-MM-dd HH:mm:ss"),
        DEFAULT_DATE_TIME_NANO("yyyy-MM-dd HH:mm:ss.SSS"),
        BACKSLASH_DATE("yyyy/MM/dd"),
        BACKSLASH_DATE_TIME_MINUTE("yyyy/MM/dd HH:mm"),
        BACKSLASH_DATE_TIME("yyyy/MM/dd HH:mm:ss"),
        BACKSLASH_DATE_TIME_NANO("yyyy/MM/dd HH:mm:ss.SSS"),
        DOT_DATE("yyyy.MM.dd"),
        DOT_DATE_TIME_MINUTE("yyyy.MM.dd HH:mm"),
        DOT_DATE_TIME("yyyy.MM.dd HH:mm:ss"),
        DOT_DATE_TIME_NANO("yyy.MM.dd HH:mm:ss.SSS"),
        CHINA_DATE("yyyy年MM月dd日"),
        CHINA_DATE_TIME_MINUTE("yyyy年MM月dd日 HH时mm分"),
        CHINA_DATE_TIME("yyyy年MM月dd日 HH时mm分ss秒"),
        ;

        private String pattern;

        FormatType(String pattern) {
            this.pattern = pattern;
        }

        public String getPattern() {
            return pattern;
        }
    }

    /**
     * localDateTime convert to Date
     *
     * @param localDateTime
     * @return Date
     */
    public static Date toDate(LocalDateTime localDateTime) {
        Instant instant = localDateTime.atZone(ZONE_ID).toInstant();
        return Date.from(instant);

    }

    /**
     * string convert to Date
     *
     * @param dateTime
     * @return Date
     */
    public static Date toDate(String dateTime) {
        LocalDateTime localDateTime = convertToLocalDateTime(dateTime);
        Instant instant = localDateTime.atZone(ZONE_ID).toInstant();
        return Date.from(instant);

    }

    /**
     * timestamp convert to Date
     *
     * @param dateTime
     * @return Date
     */
    public static Date toDate(Long dateTime) {
        LocalDateTime localDateTime = convertToLocalDateTime(dateTime);
        Instant instant = localDateTime.atZone(ZONE_ID).toInstant();
        return Date.from(instant);

    }

    /**
     * localDate convert to Date
     *
     * @param localDate
     * @return Date
     */
    public static Date toDate(LocalDate localDate) {
        LocalDateTime localDateTime = convertToLocalDateTime(localDate);
        Instant instant = localDateTime.atZone(ZONE_ID).toInstant();
        return Date.from(instant);

    }

    /**
     * datetime convert to toLocalDate
     *
     * @param datetime
     * @return Date
     */
    public static LocalDate toLocalDate(Object datetime) {
        if (datetime == null) {
            return null;
        }
        return convertToLocalDateTime(datetime).toLocalDate();
    }

    /**
     * datetime convert to toLocalDate
     *
     * @param datetime
     * @return Date
     */
    public static String toLocalDateStr(Object datetime) {
        if (datetime == null) {
            return null;
        }
        return convertToLocalDateTime(datetime).toLocalDate().toString();
    }


    /**
     * dateTime String convert to Date
     *
     * @param dateTime String dateTime
     * @param pattern  eg: yyyy-MM-dd yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
     * @return Date
     */
    public static Date toDate(String dateTime, String pattern) {
        LocalDateTime localDateTime = convertToLocalDateTime(dateTime, DateTimeFormatter.ofPattern(pattern));
        return toDate(localDateTime);
    }

    /**
     * dateTime String convert to Date
     *
     * @param dateTime   String dateTime
     * @param formatType FormatType
     * @return Date
     */
    public static Date toDate(String dateTime, FormatType formatType) {
        LocalDateTime localDateTime = convertToLocalDateTime(dateTime, DateTimeFormatter.ofPattern(formatType.pattern));
        return toDate(localDateTime);
    }

    /**
     * dateTime String convert to Date
     *
     * @param dateTime String dateTime
     * @param pattern  eg: yyyy-MM-dd yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
     * @return Date
     */
    public static Date toDate(String dateTime, DateTimeFormatter pattern) {
        LocalDateTime localDateTime = convertToLocalDateTime(dateTime, pattern);
        return toDate(localDateTime);
    }

    /**
     * Object convert to LocalDateTime, params: Timestamp、Date、LocalDate、String
     * string to date, eg:
     * "2021-05-28T",
     * "2021-05-28T05:02",
     * "2021-05-28T05:02:33",
     * "2021-05-28T05:02:33Z",
     * "2021-05-28T05:02:33.280",
     * "2021-05-28T05:02:33.280Z",
     * "20210528",
     * "202105280502",
     * "20210528050233",
     * "2021-05-28",
     * "2021-05-28 05:02",
     * "2021-05-28 05:02:33",
     * "2021-05-28 05:02:33.280",
     * "2021/05/28",
     * "2021/05/28 05:02",
     * "2021/05/28 05:02:33",
     * "2021/05/28 05:02:33.280",
     * "2021.05.28",
     * "2021.05.28 05:02",
     * "2021.05.28 05:02:33",
     * "2021.05.28 05:02:33.280",
     * "2021年05月28日",
     * "2021年05月28日 05时02分",
     * "2021年05月28日 05时02分33秒"
     *
     * @param dateTime LocalDateTime、LocalDate、Date、Timestamp、String
     * @return LocalDateTime
     */
    private static LocalDateTime convertToLocalDateTime(Object dateTime) {
        try {
            if (dateTime instanceof LocalDateTime) {
                return (LocalDateTime) dateTime;
            } else if (dateTime instanceof LocalDate) {
                return ((LocalDate) dateTime).atStartOfDay();
            } else if (dateTime instanceof String) {
                String str = ((String) dateTime);

                if (str.length() != 8 && str.length() != 14 && isNumeric(str)) {
                    Instant instant = Instant.ofEpochMilli(Long.parseLong(str));
                    return LocalDateTime.ofInstant(instant, ZONE_ID);
                }

                if (str.contains("T") && str.contains("Z")) {
                    Instant parse = Instant.parse(str);
                    return LocalDateTime.ofInstant(parse, ZONE_ID);
                }
                if (str.contains("T")) {
                    int numberCount = getNumberCount(str);
                    if (numberCount <= 12) {
                        str = str.replaceAll("T", " ");
                    } else {
                        return LocalDateTime.parse(str, DateTimeFormatter.ISO_DATE_TIME);
                    }
                }
                str = str.replaceAll("[/年月]", "-")
                        .replaceAll("[日秒]", "")
                        .replaceAll("[时分]", ":")
                        .replaceAll("(:$)|(\\s+$)", "");
                if (str.contains(".") && !str.contains("T")) {
                    String[] split = str.split("\\s");
                    str = split[0].replaceAll("\\.", "-") + (split.length > 1 ? " " + split[1] : "");
                }
                if (!str.contains("-")) {
                    StringBuilder stringBuilder = new StringBuilder();
                    char[] chars = str.toCharArray();
                    for (int i = 0; i < chars.length; i++) {
                        stringBuilder.append(chars[i]);
                        if (i == 3 || i == 5 && i != chars.length - 1) {
                            stringBuilder.append("-");
                        }
                        if (i == 7 && i != chars.length - 1) {
                            stringBuilder.append(" ");
                        }
                        if (i == 9 || i == 11 && i != chars.length - 1) {
                            stringBuilder.append(":");
                        }
                    }
                    str = stringBuilder.toString();
                }
                if (str.contains(".")) {
                    return LocalDateTime.parse(str, DateTimeFormatter.ofPattern(FormatType.DEFAULT_DATE_TIME_NANO.pattern));
                }
                String[] timeSplits = str.split(":");
                String format = timeSplits.length == 1 ?
                        (str.length() == 7 ? FormatType.DEFAULT_DATE_MONTH.pattern :  FormatType.DEFAULT_DATE.pattern)
                        : (timeSplits.length == 2 ?
                        FormatType.DEFAULT_DATE_TIME_MINUTE.pattern : FormatType.DEFAULT_DATE_TIME.pattern);
                if (FormatType.DEFAULT_DATE.pattern.equals(format)) {
                    LocalDate localDate = LocalDate.parse(str, DateTimeFormatter.ofPattern(format));
                    return localDate.atStartOfDay();
                } else if (FormatType.DEFAULT_DATE_MONTH.pattern.equals(format)) {
                    LocalDate localDate = LocalDate.parse(str + "-01");
                    return localDate.atStartOfDay();
                } else {
                    return LocalDateTime.parse(str, DateTimeFormatter.ofPattern(format));
                }
            } else if (dateTime instanceof Long) {
                Instant instant = Instant.ofEpochMilli((Long) dateTime);
                return LocalDateTime.ofInstant(instant, ZONE_ID);
            } else if (dateTime instanceof Date) {
                Instant instant = ((Date) dateTime).toInstant();
                return LocalDateTime.ofInstant(instant, ZONE_ID);
            } else {
                throw new IllegalArgumentException("not support param type !");
            }
        } catch (Exception e) {
            throw new IllegalArgumentException("not support param type !");
        }
    }

    /**
     * String Date convert LocalDateTime
     *
     * @param str
     * @param pattern
     * @return
     */
    private static LocalDateTime convertToLocalDateTime(String str, Object pattern) {
        try {

            int numberCount = getNumberCount(str);

            if (numberCount <= 8) {
                LocalDate localDate = LocalDate.parse(str, (DateTimeFormatter) pattern);
                Instant instant = localDate.atStartOfDay(ZONE_ID).toInstant();
                return LocalDateTime.ofInstant(instant, ZONE_ID);
            } else if (str.contains("T") && str.contains("Z")) {
                Instant parse = Instant.parse(str);
                return LocalDateTime.ofInstant(parse, ZONE_ID);
            } else if (str.contains("T")) {
                LocalDateTime.parse(str, DateTimeFormatter.ISO_DATE_TIME);
            }
            return LocalDateTime.parse(str, (DateTimeFormatter) pattern);

        } catch (Exception e) {
            throw new IllegalArgumentException("not support param type !");
        }
    }

    /**
     * dateTime String convert to LocalDateTime
     *
     * @param dateTime String dateTime
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(String dateTime) {
        return convertToLocalDateTime(dateTime);
    }

    /**
     * date convert to LocalDateTime
     *
     * @param date date
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        return convertToLocalDateTime(date);
    }

    /**
     * timestamp convert to LocalDateTime
     *
     * @param timestamp timestamp
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Long timestamp) {
        return convertToLocalDateTime(timestamp);
    }

    /**
     * dateTime String convert to LocalDateTime
     *
     * @param localDate localDate
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(LocalDate localDate) {
        return convertToLocalDateTime(localDate);
    }

    /**
     * dateTime String convert to LocalDateTime
     *
     * @param dateTime String dateTime
     * @param pattern  eg: yyyy-MM-dd yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(String dateTime, String pattern) {
        return convertToLocalDateTime(dateTime, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * dateTime String convert to LocalDateTime
     *
     * @param dateTime   String dateTime
     * @param formatType FormatType
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(String dateTime, FormatType formatType) {
        return convertToLocalDateTime(dateTime, DateTimeFormatter.ofPattern(formatType.pattern));
    }


    /**
     * localDateTime to default date string
     *
     * @param localDateTime LocalDateTime、LocalDate、Date、Timestamp、String
     * @return
     */
    public static String toString(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return null;
        }
        return localDateTime.format(DateTimeFormatter.ofPattern(FormatType.DEFAULT_DATE_TIME.pattern));
    }

    /**
     * date to default date string
     *
     * @param date
     * @return
     */
    public static String toString(Date date) {
        if (date == null) {
            return null;
        }
        LocalDateTime localDateTime = convertToLocalDateTime(date);
        return localDateTime.format(DateTimeFormatter.ofPattern(FormatType.DEFAULT_DATE_TIME.pattern));
    }

    /**
     * timeStamp convert to default date string
     *
     * @param timeStamp
     * @return
     */
    public static String toString(Long timeStamp) {
        if (timeStamp == null) {
            return null;
        }
        LocalDateTime localDateTime = convertToLocalDateTime(timeStamp);
        return localDateTime.format(DateTimeFormatter.ofPattern(FormatType.DEFAULT_DATE_TIME.pattern));
    }

    /**
     * localDate convert to default date string
     *
     * @param localDate
     * @return
     */
    public static String toString(LocalDate localDate) {
        if (localDate == null) {
            return null;
        }
        LocalDateTime localDateTime = convertToLocalDateTime(localDate);
        return localDateTime.format(DateTimeFormatter.ofPattern(FormatType.DEFAULT_DATE_TIME.pattern));
    }


    /**
     * Convert to pattern format
     * refer to: java.time.format.DateTimeFormatter
     *
     * @param dateTime Date
     * @param pattern  eg: "[yyyy年MM月], [yyyy-MM-dd HH:mm:ss]"
     * @return
     */
    public static String toString(Date dateTime, String pattern) {
        if (dateTime == null) {
            return null;
        }
        LocalDateTime localDateTime = convertToLocalDateTime(dateTime);
        if (pattern.contains("Z")) {
            localDateTime = localDateTime.atZone(ZONE_ID).withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime();

        }
        return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * Convert to pattern format
     * refer to: java.time.format.DateTimeFormatter
     *
     * @param dateTime   Date
     * @param formatType FormatType
     * @return
     */
    public static String toString(Date dateTime, FormatType formatType) {
        if (dateTime == null) {
            return null;
        }
        LocalDateTime localDateTime = convertToLocalDateTime(dateTime);
        if (formatType.pattern.contains("Z")) {
            localDateTime = localDateTime.atZone(ZONE_ID).withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime();

        }
        return localDateTime.format(DateTimeFormatter.ofPattern(formatType.pattern));
    }

    /**
     * Convert to pattern format
     * refer to: java.time.format.DateTimeFormatter
     *
     * @param dateTime LocalDateTime
     * @param pattern  eg: "[yyyy年MM月], [yyyy-MM-dd HH:mm:ss]"
     * @return
     */
    public static String toString(LocalDateTime dateTime, String pattern) {
        if (dateTime == null) {
            return null;
        }
        LocalDateTime localDateTime = convertToLocalDateTime(dateTime);
        if (pattern.contains("Z")) {
            localDateTime = localDateTime.atZone(ZONE_ID).withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime();

        }
        return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * Convert to pattern format
     * refer to: java.time.format.DateTimeFormatter
     *
     * @param dateTime   LocalDateTime
     * @param formatType FormatType
     * @return
     */
    public static String toString(LocalDateTime dateTime, FormatType formatType) {
        if (dateTime == null) {
            return null;
        }
        LocalDateTime localDateTime = convertToLocalDateTime(dateTime);
        if (formatType.pattern.contains("Z")) {
            localDateTime = localDateTime.atZone(ZONE_ID).withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime();

        }
        return localDateTime.format(DateTimeFormatter.ofPattern(formatType.pattern));
    }

    /**
     * year of the dateTime
     *
     * @param localDateTime
     * @return
     */
    public static int getYear(LocalDateTime localDateTime) {
        return localDateTime.getYear();
    }

    /**
     * month of the dateTime
     *
     * @param localDateTime
     * @return
     */
    public static int getMonth(LocalDateTime localDateTime) {
        return localDateTime.getMonthValue();
    }

    /**
     * week of dateTime
     *
     * @param localDateTime
     * @return
     */
    public static int getWeek(LocalDateTime localDateTime) {
        WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY, 1);
        return localDateTime.get(weekFields.weekOfYear());
    }

    /**
     * day of the localDateTime
     *
     * @param localDateTime
     * @return
     */
    public static int getDay(LocalDateTime localDateTime) {
        return localDateTime.getDayOfMonth();
    }

    /**
     * hour of the localDateTime
     *
     * @param localDateTime
     * @return
     */
    public static int getHour(LocalDateTime localDateTime) {
        return localDateTime.getHour();
    }

    /**
     * minute of the localDateTime
     *
     * @param localDateTime
     * @return
     */
    public static int getMinute(LocalDateTime localDateTime) {
        return localDateTime.getMinute();
    }

    /**
     * second of the localDateTime
     *
     * @param localDateTime
     * @return
     */
    public static int getSecond(LocalDateTime localDateTime) {
        return localDateTime.getSecond();
    }


    /**
     * nanosecond of the dateTime
     *
     * @param localDateTime
     * @return
     */
    public static long getNano(LocalDateTime localDateTime) {
        return localDateTime.getNano();
    }

    /**
     * day of the week
     * MONDAY-1 TUESDAY-2 WEDNESDAY-3 THURSDAY-4 FRIDAY-5 SATURDAY-6 SUNDAY-7
     *
     * @param localDateTime
     * @return
     */
    public static int getDayOfWeek(LocalDateTime localDateTime) {
        return localDateTime.getDayOfWeek().getValue();
    }


    /**
     * day of the year
     *
     * @param localDateTime
     * @return
     */
    public static int getDayOfYear(LocalDateTime localDateTime) {
        return localDateTime.getDayOfYear();
    }

    /**
     * millisecond of the localDateTime
     *
     * @param localDateTime
     * @return
     */
    public static long getTimeStamp(LocalDateTime localDateTime) {
        return localDateTime.atZone(ZONE_ID).toInstant().toEpochMilli();
    }

    /**
     * plus operation with year
     *
     * @param localDateTime
     * @param years
     * @return LocalDateTime
     */
    public static LocalDateTime plusYears(LocalDateTime localDateTime, int years) {
        return localDateTime.plusYears(years);
    }

    /**
     * plus operation with month
     *
     * @param localDateTime
     * @param months
     * @return LocalDateTime
     */
    public static LocalDateTime plusMouths(LocalDateTime localDateTime, int months) {
        return localDateTime.plusMonths(months);
    }

    /**
     * plus operation with month
     *
     * @param localDateTime
     * @param days
     * @return LocalDateTime
     */
    public static LocalDateTime plusDays(LocalDateTime localDateTime, int days) {
        return localDateTime.plusDays(days);
    }

    /**
     * plus operation with hour
     *
     * @param localDateTime
     * @param hours
     * @return LocalDateTime
     */
    public static LocalDateTime plusHours(LocalDateTime localDateTime, int hours) {
        return localDateTime.plusHours(hours);
    }

    /**
     * plus operation with minute
     *
     * @param localDateTime
     * @param minutes
     * @return LocalDateTime
     */
    public static LocalDateTime plusMinutes(LocalDateTime localDateTime, int minutes) {
        return localDateTime.plusMinutes(minutes);
    }

    /**
     * plus operation with second
     *
     * @param localDateTime
     * @param seconds
     * @return LocalDateTime
     */
    public static LocalDateTime plusSeconds(LocalDateTime localDateTime, int seconds) {
        return localDateTime.plusSeconds(seconds);
    }

    /**
     * plus operation with nano
     *
     * @param localDateTime
     * @param nanos
     * @return LocalDateTime
     */
    public static LocalDateTime plusNanos(LocalDateTime localDateTime, int nanos) {
        return localDateTime.plusNanos(nanos);
    }

    /**
     * plus operation with nano
     *
     * @param localDateTime
     * @param weeks
     * @return LocalDateTime
     */
    public static LocalDateTime plusWeeks(LocalDateTime localDateTime, int weeks) {
        return localDateTime.plusWeeks(weeks);
    }

    /**
     * minus operation with year
     *
     * @param localDateTime
     * @param years
     * @return LocalDateTime
     */
    public static LocalDateTime minusYears(LocalDateTime localDateTime, int years) {
        return localDateTime.minusYears(years);
    }

    /**
     * minus operation with month
     *
     * @param localDateTime
     * @param months
     * @return LocalDateTime
     */
    public static LocalDateTime minusMouths(LocalDateTime localDateTime, int months) {
        return localDateTime.minusMonths(months);
    }

    /**
     * minus operation with month
     *
     * @param localDateTime
     * @param days
     * @return LocalDateTime
     */
    public static LocalDateTime minusDays(LocalDateTime localDateTime, int days) {
        return localDateTime.minusDays(days);
    }

    /**
     * minus operation with hour
     *
     * @param localDateTime
     * @param hours
     * @return LocalDateTime
     */
    public static LocalDateTime minusHours(LocalDateTime localDateTime, int hours) {
        return localDateTime.minusHours(hours);
    }

    /**
     * minus operation with minute
     *
     * @param localDateTime
     * @param minutes
     * @return LocalDateTime
     */
    public static LocalDateTime minusMinutes(LocalDateTime localDateTime, int minutes) {
        return localDateTime.minusMinutes(minutes);
    }

    /**
     * minus operation with second
     *
     * @param localDateTime
     * @param seconds
     * @return LocalDateTime
     */
    public static LocalDateTime minusSeconds(LocalDateTime localDateTime, int seconds) {
        return localDateTime.minusSeconds(seconds);
    }

    /**
     * minus operation with nano
     *
     * @param localDateTime
     * @param nanos
     * @return LocalDateTime
     */
    public static LocalDateTime minusNanos(LocalDateTime localDateTime, int nanos) {
        return localDateTime.minusNanos(nanos);
    }

    /**
     * minus operation with nano
     *
     * @param localDateTime
     * @param weeks
     * @return LocalDateTime
     */
    public static LocalDateTime minusWeeks(LocalDateTime localDateTime, int weeks) {
        return localDateTime.minusWeeks(weeks);
    }

    /**
     * judge param1 before param2
     *
     * @param before
     * @param current
     * @return
     */
    public static Boolean lt(LocalDateTime before, LocalDateTime current) {
        return before.isBefore(current);
    }

    /**
     * judge param1 before or equal param2
     *
     * @param before
     * @param current
     * @return
     */
    public static Boolean le(LocalDateTime before, LocalDateTime current) {
        return before.isBefore(current) || before.isEqual(current);
    }

    /**
     * judge param1 after param2
     *
     * @param after
     * @param current
     * @return
     */
    public static Boolean gt(LocalDateTime after, LocalDateTime current) {
        return after.isAfter(current);
    }


    /**
     * judge param1 after or param2
     *
     * @param after
     * @param current
     * @return
     */
    public static Boolean ge(LocalDateTime after, LocalDateTime current) {
        return after.isAfter(current) || after.isEqual(current);
    }

    /**
     * judge param1 equal param2
     *
     * @param dateTime
     * @param current
     * @return
     */
    public static Boolean eq(LocalDateTime dateTime, LocalDateTime current) {
        return dateTime.isEqual(current);
    }

    /**
     * current datetime
     *
     * @return
     */
    public static LocalDateTime now() {
        return LocalDateTime.now(ZONE_ID);
    }

    /**
     * current datetime string
     *
     * @return
     */
    public static String nowToString() {
        return toString(LocalDateTime.now(ZONE_ID));
    }

    /**
     * current datetime string
     *
     * @return
     */
    public static String nowToString(String pattern) {
        return toString(LocalDateTime.now(ZONE_ID), pattern);
    }

    /**
     * current datetime string
     *
     * @return
     */
    public static String nowToString(FormatType formatType) {
        return toString(LocalDateTime.now(ZONE_ID), formatType);
    }

    /**
     * current date
     *
     * @param dateTime
     * @return
     */
    public static LocalDate getLocalDate(LocalDateTime dateTime) {
        return convertToLocalDateTime(dateTime).toLocalDate();
    }


    /**
     * get months time different
     *
     * @param startInclusive
     * @param endInclusive
     * @return
     */
    public static long getDurationMonths(LocalDateTime startInclusive, LocalDateTime endInclusive) {
        int startTotalMonths = startInclusive.getYear() * 12 + startInclusive.getMonthValue();
        int endTotalMonths = endInclusive.getYear() * 12 + endInclusive.getMonthValue();
        return startTotalMonths - endTotalMonths;
    }

    /**
     * get days time different
     *
     * @param startInclusive
     * @param endInclusive
     * @return
     */
    public static long getDurationDays(LocalDateTime startInclusive, LocalDateTime endInclusive) {
        return Duration.between(startInclusive, endInclusive).toDays();
    }


    /**
     * get hours time different
     *
     * @param startInclusive
     * @param endInclusive
     * @return
     */
    public static long getDurationHours(LocalDateTime startInclusive, LocalDateTime endInclusive) {
        return Duration.between(startInclusive, endInclusive).toHours();
    }

    /**
     * get minutes time different
     *
     * @param startInclusive
     * @param endInclusive
     * @return
     */
    public static long getDurationMinutes(LocalDateTime startInclusive, LocalDateTime endInclusive) {
        return Duration.between(startInclusive, endInclusive).toMinutes();
    }

    /**
     * get seconds time different
     *
     * @param startInclusive
     * @param endInclusive
     * @return
     */
    public static long getDurationSeconds(LocalDateTime startInclusive, LocalDateTime endInclusive) {
        return Duration.between(startInclusive, endInclusive).toMillis() / 1000;
    }

    /**
     * get millis time different
     *
     * @param startInclusive
     * @param endInclusive
     * @return
     */
    public static long getDurationMillis(LocalDateTime startInclusive, LocalDateTime endInclusive) {
        return Duration.between(startInclusive, endInclusive).toMillis();
    }

    /**
     * get Nanos time different
     *
     * @param startInclusive
     * @param endInclusive
     * @return
     */
    public static long getDurationNanos(LocalDateTime startInclusive, LocalDateTime endInclusive) {
        return Duration.between(startInclusive, endInclusive).toNanos();
    }

    /**
     * 获取字符串日期中的数字长度
     *
     * @param str
     * @return
     */
    private static int getNumberCount(String str) {
        char[] chars = str.toCharArray();
        int count = 0;
        for (char ch : chars) {
            if (ch >= 48 && ch <= 57) {
                ++count;
            }
        }
        return count;
    }

    /**
     * appoint time begin time
     *
     * @return
     */
    public static LocalDateTime appointTimeBeginTime(LocalDateTime localDateTime) {
        return LocalDateTime.of(localDateTime.toLocalDate(), LocalTime.MIN);
    }

    /**
     * appoint time end Time
     *
     * @return
     */
    public static LocalDateTime appointTimeEndTime(LocalDateTime localDateTime) {
        return LocalDateTime.of(localDateTime.toLocalDate(), LocalTime.MAX);
    }

    /**
     * JANUARY-1
     * FEBRUARY-2
     * MARCH-3
     * APRIL-4
     * MAY-5
     * JUNE-6
     * JULY-7
     * AUGUST-8
     * SEPTEMBER-9
     * OCTOBER-10
     * NOVEMBER-11
     * DECEMBER-12
     * <p>
     * appoint month begin time
     *
     * @param month
     * @return
     */
    public static LocalDateTime appointMonthBeginTime(int month) {
        LocalDate now = LocalDate.now(ZONE_ID);
        return LocalDateTime.of(LocalDate.of(now.getYear(), Month.of(month), 1), LocalTime.MIN);
    }

    /**
     * appoint month end time
     *
     * @param month
     * @return
     */
    public static LocalDateTime appointMonthEndTime(int month) {
        LocalDate now = LocalDate.now(ZONE_ID);
        Month months = Month.of(month);
        return LocalDateTime.of(LocalDate.of(now.getYear(), months, months.length(now.isLeapYear())), LocalTime.MAX);
    }

    /**
     * appoint last year of month begin time
     *
     * @param month
     * @return
     */
    public static LocalDateTime appointLastYearOfMonthBeginTime(int month) {
        LocalDate now = LocalDate.now(ZONE_ID).minus(1L, ChronoUnit.YEARS);
        ;
        return LocalDateTime.of(LocalDate.of(now.getYear(), Month.of(month), 1), LocalTime.MIN);
    }

    /**
     * appoint appoint last year of month end time
     *
     * @param month
     * @return
     */
    public static LocalDateTime appointLastYearOfMonthEndTime(int month) {
        LocalDate now = LocalDate.now(ZONE_ID).minus(1L, ChronoUnit.YEARS);
        Month months = Month.of(month);
        return LocalDateTime.of(LocalDate.of(now.getYear(), months, months.length(now.isLeapYear())), LocalTime.MAX);
    }

    /**
     * appoint next year of month begin time
     *
     * @param month
     * @return
     */
    public static LocalDateTime appointNextYearOfMonthBeginTime(int month) {
        LocalDate now = LocalDate.now(ZONE_ID).plus(1L, ChronoUnit.YEARS);
        ;
        return LocalDateTime.of(LocalDate.of(now.getYear(), Month.of(month), 1), LocalTime.MIN);
    }

    /**
     * appoint appoint next year of month end time
     *
     * @param month
     * @return
     */
    public static LocalDateTime appointNextYearOfMonthEndTime(int month) {
        LocalDate now = LocalDate.now(ZONE_ID).plus(1L, ChronoUnit.YEARS);
        Month months = Month.of(month);
        return LocalDateTime.of(LocalDate.of(now.getYear(), months, months.length(now.isLeapYear())), LocalTime.MAX);
    }


    /**
     * appoint last few day begin time
     *
     * @param fewDay
     * @return
     */
    public static LocalDateTime appointLastFewDayBeginTime(int fewDay) {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).minus(fewDay - 1, ChronoUnit.DAYS), LocalTime.MIN);
    }

    /**
     * appoint last few day end time
     *
     * @param fewDay
     * @return
     */
    public static LocalDateTime appointLastFewDayEndTime(int fewDay) {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).minus(fewDay - 1, ChronoUnit.DAYS), LocalTime.MAX);
    }

    /**
     * today begin time
     *
     * @return
     */
    public static LocalDateTime todayBeginTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID), LocalTime.MIN);
    }

    /**
     * today end Time
     *
     * @return
     */
    public static LocalDateTime todayEndTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID), LocalTime.MAX);
    }

    /**
     * yesterday begin time
     *
     * @return
     */
    public static LocalDateTime yesterdayBeginTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).minus(1L, ChronoUnit.DAYS), LocalTime.MIN);
    }

    /**
     * yesterday end time
     *
     * @return
     */
    public static LocalDateTime yesterdayEndTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).minus(1L, ChronoUnit.DAYS), LocalTime.MAX);
    }

    /**
     * last 7 days begin time
     *
     * @return
     */
    public static LocalDateTime last7DaysBeginTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).minus(6L, ChronoUnit.DAYS), LocalTime.MIN);
    }

    /**
     * last 7 days end time
     *
     * @return
     */
    public static LocalDateTime last7DaysEndTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID), LocalTime.MAX);
    }

    /**
     * last 30 days begin time
     *
     * @return
     */
    public static LocalDateTime last30DaysBeginTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).minus(29L, ChronoUnit.DAYS), LocalTime.MIN);
    }

    /**
     * last 30 days end time
     *
     * @return
     */
    public static LocalDateTime last30DaysEndTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID), LocalTime.MAX);
    }

    /**
     * last 1 years begin time
     *
     * @return
     */
    public static LocalDateTime last1YearBeginTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).minus(1L, ChronoUnit.YEARS).plus(1L, ChronoUnit.DAYS), LocalTime.MIN);
    }

    /**
     * last 1 years end time
     *
     * @return
     */
    public static LocalDateTime last1YearEndTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID), LocalTime.MAX);
    }

    /**
     * week begin time
     *
     * @return
     */
    public static LocalDateTime weekBeginTime() {
        LocalDate localDate = LocalDate.now(ZONE_ID);
        return LocalDateTime.of(localDate.minusDays(localDate.getDayOfWeek().getValue() - 1), LocalTime.MIN);
    }

    /**
     * week end time
     *
     * @return
     */
    public static LocalDateTime weekEndTime() {
        LocalDate localDate = LocalDate.now(ZONE_ID);
        return LocalDateTime.of(localDate.plusDays(7 - localDate.getDayOfWeek().getValue()), LocalTime.MAX);
    }

    /**
     * month begin time
     *
     * @return
     */
    public static LocalDateTime monthBeginTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).with(TemporalAdjusters.firstDayOfMonth()), LocalTime.MIN);
    }

    /**
     * month end time
     *
     * @return
     */
    public static LocalDateTime monthEndTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).with(TemporalAdjusters.lastDayOfMonth()), LocalTime.MAX);
    }

    /**
     * quarter begin time
     *
     * @return
     */
    public static LocalDateTime quarterBeginTime() {
        LocalDate localDate = LocalDate.now(ZONE_ID);
        Month month = Month.of(localDate.getMonth().firstMonthOfQuarter().getValue());
        return LocalDateTime.of(LocalDate.of(localDate.getYear(), month, 1), LocalTime.MIN);
    }

    /**
     * quarter end time
     *
     * @return
     */
    public static LocalDateTime quarterEndTime() {
        LocalDate localDate = LocalDate.now(ZONE_ID);
        Month month = Month.of(localDate.getMonth().firstMonthOfQuarter().getValue()).plus(2L);
        return LocalDateTime.of(LocalDate.of(localDate.getYear(), month, month.length(localDate.isLeapYear())), LocalTime.MAX);
    }

    /**
     * appoint quarter begin time
     * @param localDateTime
     * @return
     */
    public static LocalDateTime appointQuarterStart(LocalDateTime localDateTime) {
        // 获取当前日期的年份和月份
        int year = localDateTime.getYear();
        int month = localDateTime.getMonthValue();

        // 计算季度开始月份
        int quarterStartMonth = ((month - 1) / 3) * 3 + 1;

        // 获取季度开始时间
        YearMonth yearMonth = YearMonth.of(year, quarterStartMonth);
        return LocalDateTime.of(yearMonth.atDay(1), LocalTime.MIN);
    }

    /**
     * appoint quarter end time
     * @param localDateTime
     * @return
     */
    public static LocalDateTime appointQuarterEnd(LocalDateTime localDateTime) {
        // 获取当前日期的年份和月份
        int year = localDateTime.getYear();
        int month = localDateTime.getMonthValue();

        // 计算季度结束月份
        int quarterEndMonth = ((month - 1) / 3 + 1) * 3;

        // 获取季度结束时间
        YearMonth yearMonth = YearMonth.of(year, quarterEndMonth);
        return LocalDateTime.of(yearMonth.atEndOfMonth(), LocalTime.MAX);
    }


/**
     * half year begin time
     *
     * @return
     */
    public static LocalDateTime halfYearBeginTime() {
        LocalDate now = LocalDate.now(ZONE_ID);
        Month month = (now.getMonthValue() > 6) ? Month.JULY : Month.JANUARY;
        return LocalDateTime.of(LocalDate.of(now.getYear(), month, 1), LocalTime.MIN);
    }

    /**
     * half year end time
     *
     * @return
     */
    public static LocalDateTime halfYearEndTime() {
        LocalDate now = LocalDate.now(ZONE_ID);
        Month month = (now.getMonthValue() > 6) ? Month.DECEMBER : Month.JUNE;
        return LocalDateTime.of(LocalDate.of(now.getYear(), month, month.length(now.isLeapYear())), LocalTime.MAX);
    }

    /**
     * year begin time
     *
     * @return
     */
    public static LocalDateTime yearBeginTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).with(TemporalAdjusters.firstDayOfYear()), LocalTime.MIN);
    }

    /**
     * year end time
     *
     * @return
     */
    public static LocalDateTime yearEndTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).with(TemporalAdjusters.lastDayOfYear()), LocalTime.MAX);
    }

    /**
     * last week begin time
     *
     * @return
     */
    public static LocalDateTime lastWeekBeginTime() {
        LocalDate lastWeek = LocalDate.now(ZONE_ID).minus(1L, ChronoUnit.WEEKS);
        return LocalDateTime.of(lastWeek.minusDays(lastWeek.getDayOfWeek().getValue() - 1), LocalTime.MIN);
    }

    /**
     * last ween end time
     *
     * @return
     */
    public static LocalDateTime lastWeekEndTime() {
        LocalDate lastWeek = LocalDate.now(ZONE_ID).minus(1L, ChronoUnit.WEEKS);
        return LocalDateTime.of(lastWeek.plusDays(7 - lastWeek.getDayOfWeek().getValue()), LocalTime.MAX);
    }

    /**
     * last month begin time
     *
     * @return
     */
    public static LocalDateTime lastMonthBeginTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).minus(1L, ChronoUnit.MONTHS).with(TemporalAdjusters.firstDayOfMonth()), LocalTime.MIN);
    }

    /**
     * last month end time
     *
     * @return
     */
    public static LocalDateTime lastMonthEndTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).minus(1L, ChronoUnit.MONTHS).with(TemporalAdjusters.lastDayOfMonth()), LocalTime.MAX);
    }

    /**
     * last quarter beginTime
     *
     * @return
     */
    public static LocalDateTime lastQuarterBeginTime() {
        LocalDate now = LocalDate.now(ZONE_ID);
        Month lastMonth = Month.of(now.getMonth().firstMonthOfQuarter().getValue()).minus(3L);
        int yearOfLastQuarter = lastMonth.getValue() < 4 ? now.getYear() - 1 : now.getYear();
        return LocalDateTime.of(LocalDate.of(yearOfLastQuarter, lastMonth, 1), LocalTime.MIN);
    }

    /**
     * last quarter endTime
     *
     * @return
     */
    public static LocalDateTime lastQuarterEndTime() {
        LocalDate now = LocalDate.now(ZONE_ID);
        Month lastMonth = Month.of(now.getMonth().firstMonthOfQuarter().getValue()).minus(1L);
        int yearOfLastQuarter = lastMonth.getValue() < 4 ? now.getYear() - 1 : now.getYear();
        return LocalDateTime.of(LocalDate.of(yearOfLastQuarter, lastMonth, lastMonth.length(now.isLeapYear())), LocalTime.MAX);
    }

    /**
     * last half year begin time
     *
     * @return
     */
    public static LocalDateTime lastHalfYearBeginTime() {
        LocalDate now = LocalDate.now(ZONE_ID);
        int lastHalfYear = (now.getMonthValue() > 6) ? now.getYear() : now.getYear() - 1;
        Month firstMonthOfLastHalfYear = (now.getMonthValue() > 6) ? Month.JANUARY : Month.JULY;
        return LocalDateTime.of(LocalDate.of(lastHalfYear, firstMonthOfLastHalfYear, 1), LocalTime.MIN);

    }

    /**
     * last half year end time
     *
     * @return
     */
    public static LocalDateTime lastHalfYearEndTime() {
        LocalDate now = LocalDate.now(ZONE_ID);
        int lastHalfYear = (now.getMonthValue() > 6) ? now.getYear() : now.getYear() - 1;
        Month lastMonthOfLastHalfYear = (now.getMonthValue() > 6) ? Month.JUNE : Month.DECEMBER;
        return LocalDateTime.of(LocalDate.of(lastHalfYear, lastMonthOfLastHalfYear, lastMonthOfLastHalfYear.length(now.isLeapYear())), LocalTime.MAX);

    }

    /**
     * last year beginTime
     *
     * @return
     */
    public static LocalDateTime lastYearBeginTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).minus(1L, ChronoUnit.YEARS).with(TemporalAdjusters.firstDayOfYear()), LocalTime.MIN);
    }

    /**
     * last year endTime
     *
     * @return
     */
    public static LocalDateTime lastYearEndTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).minus(1L, ChronoUnit.YEARS).with(TemporalAdjusters.lastDayOfYear()), LocalTime.MAX);
    }

    /**
     * next week begin time
     *
     * @return
     */
    public static LocalDateTime nextWeekBeginTime() {
        LocalDate nextWeek = LocalDate.now(ZONE_ID).plus(1L, ChronoUnit.WEEKS);
        return LocalDateTime.of(nextWeek.minusDays(nextWeek.getDayOfWeek().getValue() - 1), LocalTime.MIN);
    }

    /**
     * next week end time
     *
     * @return
     */
    public static LocalDateTime nextWeekEndTime() {
        LocalDate nextWeek = LocalDate.now(ZONE_ID).plus(1L, ChronoUnit.WEEKS);
        return LocalDateTime.of(nextWeek.plusDays(7 - nextWeek.getDayOfWeek().getValue()), LocalTime.MAX);
    }

    /**
     * last month begin time
     *
     * @return
     */
    public static LocalDateTime nextMonthBeginTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).plus(1L, ChronoUnit.MONTHS).with(TemporalAdjusters.firstDayOfMonth()), LocalTime.MIN);
    }

    /**
     * last month end time
     *
     * @return
     */
    public static LocalDateTime nextMonthEndTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).plus(1L, ChronoUnit.MONTHS).with(TemporalAdjusters.lastDayOfMonth()), LocalTime.MAX);
    }

    /**
     * next quarter begin time
     *
     * @return
     */
    public static LocalDateTime nextQuarterBeginTime() {
        LocalDate now = LocalDate.now(ZONE_ID);
        Month firstMonthOfQuarter = Month.of(now.getMonth().firstMonthOfQuarter().getValue());
        Month firstMonthOfNextQuarter = firstMonthOfQuarter.plus(3L);
        int yearOfNextQuarter = firstMonthOfQuarter.getValue() > 9 ? now.getYear() + 1 : now.getYear();
        return LocalDateTime.of(LocalDate.of(yearOfNextQuarter, firstMonthOfNextQuarter, 1), LocalTime.MIN);
    }

    /**
     * next quarter end time
     *
     * @return
     */
    public static LocalDateTime nextQuarterEndTime() {
        LocalDate now = LocalDate.now(ZONE_ID);
        Month firstMonthOfQuarter = Month.of(now.getMonth().firstMonthOfQuarter().getValue());
        Month firstMonthOfNextQuarter = firstMonthOfQuarter.plus(5L);
        int yearOfNextQuarter = firstMonthOfQuarter.getValue() > 9 ? now.getYear() + 1 : now.getYear();
        return LocalDateTime.of(LocalDate.of(yearOfNextQuarter, firstMonthOfNextQuarter, firstMonthOfNextQuarter.maxLength()), LocalTime.MAX);
    }

    /**
     * next half year begin time
     *
     * @return
     */
    public static LocalDateTime nextHalfYearBeginTime() {
        LocalDate now = LocalDate.now(ZONE_ID);
        int nextHalfYear = now.getMonthValue() > 6 ? now.getYear() + 1 : now.getYear();
        Month firstMonthOfNextHalYear = now.getMonthValue() > 6 ? Month.JANUARY : Month.JULY;
        return LocalDateTime.of(LocalDate.of(nextHalfYear, firstMonthOfNextHalYear, 1), LocalTime.MIN);
    }

    /**
     * next half year end time
     *
     * @return
     */
    public static LocalDateTime nextHalfYearEndTime() {
        LocalDate now = LocalDate.now(ZONE_ID);
        int nextHalfYear = now.getMonthValue() > 6 ? now.getYear() + 1 : now.getYear();
        Month lastMonthOfNextHalfYear = now.getMonthValue() > 6 ? Month.JUNE : Month.DECEMBER;
        return LocalDateTime.of(LocalDate.of(nextHalfYear, lastMonthOfNextHalfYear, lastMonthOfNextHalfYear.length(now.isLeapYear())), LocalTime.MAX);
    }

    /**
     * next year begin time
     *
     * @return
     */
    public static LocalDateTime nextYearBeginTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).plus(1, ChronoUnit.YEARS).with(TemporalAdjusters.firstDayOfYear()), LocalTime.MIN);
    }

    /**
     * next year end time
     *
     * @return
     */
    public static LocalDateTime nextYearEndTime() {
        return LocalDateTime.of(LocalDate.now(ZONE_ID).plus(1, ChronoUnit.YEARS).with(TemporalAdjusters.lastDayOfYear()), LocalTime.MAX);
    }

    /**
     * timestamp str
     *
     * @param str
     * @return
     */
    public static boolean isNumeric(String str) {
        for (int i = str.length(); --i >= 0; ) {
            if (!Character.isDigit(str.charAt(i))) {
                return false;
            }
        }
        return true;
    }


}
